package org.proxywars.engine.hud.valuebar;

import java.util.ArrayList;

import org.proxywars.engine.scene.shape.FilledRectangle3d;

import com.jme.renderer.ColorRGBA;


/**
 * This class is a layer for a value bar.
 * It is designed to act as a simple background or color layer for a ValueBar.
 * <p>
 * This layer can also add a texture to the bar and tile or stretch it, but it does not 
 * support slicing the image into 9 regions for high control over the texture.
 * <p>
 * For really good looking bars, use a Sliced Layer above and below this layer 
 * and simple fill this layer with a color, color range, or perhaps a gradient texture. * 
 * 
 * @author Steven DeVries
 */
public class ValueBarLayer    extends FilledRectangle3d 
                                                 implements InterfaceValueBarLayer {
    /* class (static) variables */
    private static final long serialVersionUID = 1L;
    
    /* public variables */    
    
    /* protected variables */    
    protected int min = 0;                 // Bar values
    protected int max = 100;
    protected int current = 100;
    protected float percent = 1f;       // Not multiplied by 100   
    
    /* package level (no access modifier) variables */
    /* private variables */
   
    
    /* Constructors */
    /**
     * Convenience constructor for child classes.
     * Should only ever be used on components that load a skin.
     */
    public ValueBarLayer() {
    }
    
    /**
     * Creates a 125x15 value bar layer starting at (0,0)
     * with a min of 0 and max 100;
     */
    public ValueBarLayer(String name) {
        super(name, 0, 0, 125, 15);
        initialize(0, 100);
    }
    
    /**
     * Creates a 125x15 value bar layer at (0,0) with min and max values set.
     * @param name    Name of the layer.
     * @param min       Minimum value displayed.
     * @param max      Maximum value displayed.
     */
    public ValueBarLayer(String name, int min, int max) {
        super(name, 0, 0, 125, 15);
        initialize(min, max);
    }
    
    /**
     * Creates a value bar layer at the specified location, height, width, min, and max values.
     * @param name      Name of the layer.
     * @param x           X pixel location of the top left corner.
     * @param y            Y pixel location of top left corner.
     * @param width     Width of the layer.
     * @param height    Height of the layer.
     * @param min        Minimum value displayed.
     * @param max       Maximum value displayed.
     */
    public ValueBarLayer(String name, int x, int y, int width, int height, int min, int max) {
        super(name, x, y, width, height);
        initialize(min, max);
    }
    
    /**
     * Convenience function so that all the constructors just call one function.
     * Makes it easier to change/update or for children to initialize all the values as well.
     */
    protected void initialize(int min, int max) {        
        updateFillDirection();
        setMin(min);
        setMax(max);
        setCurrent(max);
    }
    
    /**
     * Get the current value of the layer.
     * @return  the current value of the layer.
     */
    public int getCurrent() {
        return current;
    }
    
    /**
     * Get the max value of the layer.
     * @return  the max value of the layer.
     */
    public int getMax() {
        return max;
    }
    
    /**
     * Get the min value of the layer.
     * @return  the min value of the layer.
     */
    public int getMin() {
        return min;
    }    
    
    
    /* Set Methods */
    /**
     * Sets the color to paint the bar with.
     * Also turns off image texture painting.
     * 
     * @param color     RGBA color of the bar.
     */
    @Override
    public void setColor(ColorRGBA color) {
        if(     colors.isEmpty() || 
                (colors.size() ==1 && colors.get(0) != color)   ) {
            setColorRange(color, color, 1);
        }
    }

    /**
     * Sets the range of colors for this bar to use when painting itself.
     * Using the full/empty colors, the bar generates a color to paint itself with 
     * based on the current value of the bar.
     * 
     * @param emptyColor    color to display when bar is empty.
     * @param fullColor         color to display when bar is full.
     * @param numColors     number of colors to use.
     */
    @Override
    public void setColorRange(ColorRGBA emptyColor, ColorRGBA fullColor,
                                                  int numColors) {
        ArrayList<ColorRGBA> colorArray = new ArrayList<ColorRGBA>(numColors);
        colorArray.add(fullColor);
        if (numColors >1) {
            colorArray.add(emptyColor);
        }        
        setColorRange(colorArray, numColors);
    }
    
    /**
     * Sets the range of colors for this bar to use when painting itself.
     * This argument takes an array of colors so that you can go between any number of colors.
     * <p>
     * The distribution of colors is equal, meaning if you supply an array of red, yellow, and green,
     * with 5 colors, the function sets an array with 5 colors from red to yellow, 
     * and 5 from yellow to green.
     * <p>
     * To make the bar stay one color longer 
     * (e.g. green until half full, then yellow until quarter, then red)
     * Repeat the same color in the parameter array - red, yellow, green, green.
     *<p>
     * Note: the number of colors is per transition.  Meaning if the supplied array has 3 colors 
     * and numColors is 10, the resulting array will be 20 colors.  10 from color 1 to 2, 
     * and another 10 from color 2 to 3.
     * 
     * @param colorArray    Array of colors starting with empty color, ending in full color.
     * @param numColors   the number of colors per transition.
     */
    @Override
    public void setColorRange(ArrayList<ColorRGBA> colorArray, int numColors) {
        isTextured = false;
        textureState.setEnabled(false);
        
        /* Add colors to arrayList */
        if(colorArray.size() == 1) {
            colors = colorArray;
        }
        else {
            colors = new ArrayList<ColorRGBA>(numColors);            
            
            int numSourceColors = colorArray.size()-1;  // set this way because of how the loop runs
            float colorStep = (1/(float) numColors);
            float rStep, gStep, bStep, aStep;
            float r, g, b, a;
            ColorRGBA currentColor, finalColor;
            
            /* Add first color */
            colors.add(colorArray.get(0)    );
            
            /* Transition the color from one to the next and add them to the colors array */
            for(int i=0; i < numSourceColors; i++) {
                currentColor = colorArray.get(i);
                finalColor = colorArray.get(i+1);   // reason size is -1
                
                rStep = (finalColor.r - currentColor.r) * colorStep;
                gStep = (finalColor.g - currentColor.g) * colorStep;
                bStep = (finalColor.b - currentColor.b) * colorStep;
                aStep = (finalColor.a - currentColor.a) * colorStep;
                
                //System.out.println("\nColor Transistion: "+i+", "+currentColor+ " - " +finalColor);                
                for(int j=0; j<numColors; j++) {
                    r = currentColor.r + rStep;
                    g = currentColor.g + gStep;
                    b = currentColor.b + bStep;
                    a = currentColor.a + aStep;
                    
                    //Note: for some reason calling currentColor.set(r, g, b, a) doesn't actually work here.
                    currentColor = new ColorRGBA(r, g, b, a);
                    currentColor.clamp();
                    
                    //System.out.println("Adding Color : " + currentColor);
                    colors.add(currentColor);
                }
            }
            
            /*System.out.println("\nReading Color Array:");
            for(int i=0; i < colors.size(); i++) {
                ColorRGBA c = colors.get(i);
                System.out.println("Color "+i+": " + c);
            }*/
        }
        updateFill();
    }

    /**
     * Set the current value of the bar.
     * @param current       current value of the bar.
     */
    @Override
    public void setCurrent(int current) {
        /* Validity Check - current between min/max*/
        current = (current < min ? min : current); 
        current = (current > max ? max : current);
        
        /* Only update if value has changed */
        if (  current != this.current ) 
        {
            this.current = current;
            updatePercent();
        }
    }

    /**
     * Set the max value the bar can display.
     * @param max       max value bar can display.
     */
    @Override
    public void setMax(int max) {
        /* Only update if value has changed */
        if (  max != this.max ) 
        {
            this.max = max;
            updatePercent();
        }
    }

    /**
     * Set the minimum value the bar can display.
     * @param min       minimum value the bar can display.
     */
    @Override
    public void setMin(int min) {
        /* Only update if value has changed */
        if (  min != this.min ) 
        {
            this.min = min;
            updatePercent();
        }
    }

 
    /* Update Methods */
    /**
     * Automatically called when the current value is changed.
     * Updates the value of the current %.
     * It also automatically updates the fill color and size. 
     */
    protected void updatePercent()
    {
        percent = (((float) (current-min))  /  ((float) (max-min)));
        updateFillColor();
        setFillSize(percent, percent);
    }
    
    /**
     * Updates the color to fill the bar with when using a Color Range.
     */
    protected void updateFillColor()
    {
        /* Update Fill Color */
        if( !isTextured ) {
            int numColors = colors.size();
            int index = (int) (percent*numColors) -1;
            index = ( ((index < 0) || (numColors == 1)) ? 0 : index);
            ColorRGBA color = colors.get(index);
           
            setDefaultColor(color);
            //System.out.println("Color :" + (index+1)+"/" + numColors +" = " + color);
        }
    }
    
    /**
     * Automatically sets the fill directions based on bars height and width.
     * If the bar's width is greater than height, it only allows horizontal scaling.
     * If the bar's height is greater that width, it only allows vertical scaling.
     * If height and width are equal, it scales both.
     * <p>
     * <b>Note:</b> This in no way prevents anyone from manually setting the fill directions!
     * This is just a convenience function  to call if you want some fairly standard behavior.
     */
    public void updateFillDirection() {
        setFillDirection(   (width >= height ? true : false), 
                                 (height >= width ? true : false)    );
    }
}
